package com.lianqi.emcpframework.utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

/**
 * @program emcp-framework
 * @ClassName AopUtils
 * @description: aop工具类
 * @author: sky
 * @create: 2018/11/17 17:51
 */
public class AopUtils {
    /**
     * 查找方法注解
     * 
     * @param clazz
     *            方法归属类
     * @param method
     *            方法
     * @param annotation
     *            注解类型
     * @param <T>
     *            泛型
     * @return 注解
     */
    public static <T extends Annotation> T findMethodAnnotation(Class clazz, Method method, Class<T> annotation) {
        Method m = method;
        T ann = AnnotationUtils.findAnnotation(m, annotation);
        if (ann != null) {
            return ann;
        }
        m = ClassUtils.getMostSpecificMethod(m, clazz);
        ann = AnnotationUtils.findAnnotation(m, annotation);
        if (ann == null) {
            List<Class> supers = new ArrayList<>();
            supers.addAll(Arrays.asList(clazz.getInterfaces()));
            if (clazz.getSuperclass() != Object.class) {
                supers.add(clazz.getSuperclass());
            }
            for (Class c : supers) {
                if (c == null) {
                    continue;
                }
                Method[] ims = new Method[1];

                ReflectionUtils.doWithMethods(c, im -> {
                    // 从父类重写 或 接口实现方法
                    if (im.getName().endsWith(method.getName())
                        && im.getParameterCount() == method.getParameterCount()) {
                        ims[0] = im;
                    }
                });
                if (ims[0] != null) {
                    ann = findMethodAnnotation(c, ims[0], annotation);
                    if (ann != null) {
                        return ann;
                    }
                }
            }
        }
        return ann;
    }

    /**
     * 查找注解 如果方法上找不到 则从类上查找
     * 
     * @param clazz
     *            类
     * @param method
     *            方法
     * @param annotation
     *            注解类型
     * @param <T>
     *            注解泛型
     * @return 注解
     */
    public static <T extends Annotation> T findAnnotation(Class clazz, Method method, Class<T> annotation) {
        T ann = findMethodAnnotation(clazz, method, annotation);
        if (ann != null) {
            return ann;
        }
        return AnnotationUtils.findAnnotation(clazz, annotation);
    }

    /**
     * 通过aop切入点 JoinPoint 查找注解
     * 
     * @param point
     *            切入点
     * @param annotation
     *            注解类型
     * @param <T>
     *            注解泛型
     * @return 注解
     */
    public static <T extends Annotation> T findAnnotation(JoinPoint point, Class<T> annotation) {
        MethodSignature signature = (MethodSignature)point.getSignature();
        Method m = signature.getMethod();
        Class<?> clazz = point.getTarget().getClass();
        return findAnnotation(clazz, m, annotation);
    }

    /**
     * 获取拼装的方法定义
     * 
     * @param point
     *            切入点
     * @return String 方法定义字符串
     */
    public static String getMethodDefinition(JoinPoint point) {
        MethodSignature signature = (MethodSignature)point.getSignature();
        String methodName = signature.getName();
        String[] paramNames = signature.getParameterNames();
        Class[] paramsTypes = signature.getParameterTypes();
        return AssembleTools.assembleMethodDefinition(methodName, paramNames, paramsTypes);
    }

}
